package com.aop;

import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.reflect.MethodSignature;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;

public class ReflectUtil {

    private static final Logger LOGGER = LoggerFactory.getLogger(ReflectUtil.class);

    private ReflectUtil() {
    }

    private static final String START_SIGN = "#{";

    private static final String END_SIGN = "}";

    private static final String SEPARATE = "[.]";

    public static String getValueStr(String key, Map<String, Object> fieldNameAndValue) {
        if (StringUtils.isBlank(key)) {
            return key;
        }

        Object value = getValue(key.trim(), fieldNameAndValue);
        return value == null ? null : value.toString();
    }

    public static Object getValue(String key, Map<String, Object> fieldNameAndValue) {
        int startIndex = key.indexOf(START_SIGN);
        int endIndex = key.indexOf(END_SIGN);

        // 场景1.无表达式or表达式配置错误：直接返回key
        if (startIndex == -1 || endIndex == -1 || endIndex - 2 <= startIndex) {
            return key;
        }
        // 场景2.只有1个表达式：返回Object对象
        else if (startIndex == 0 && endIndex + 1 == key.length()) {
            String realKey = key.substring(startIndex + 2, endIndex);
            return getObj(realKey, fieldNameAndValue);
        }
        // 场景3.表达式(多个)+字符串：返回String
        else {
            String subRealKey = key.substring(startIndex + 2, endIndex);
            String objStr = getObjStr(subRealKey, fieldNameAndValue);
            return getValue(key.replace(START_SIGN + subRealKey + END_SIGN, objStr), fieldNameAndValue);
        }
    }

    private static Object getObj(String realKey, Map<String, Object> fieldNameAndValue) {
        Object tempObject = null;
        String[] splitArray = realKey.split(SEPARATE);
        for (int i = 0; i < splitArray.length; i++) {
            String fieldName = splitArray[i];
            if (i == 0) {
                // 第一层从nameAndValue取
                tempObject = fieldNameAndValue.get(fieldName);
                if (tempObject == null) {
                    break;
                }
            } else {
                // 第二层用对象的get方法取
                tempObject = getValueFromGetObject(tempObject, fieldName);
            }
        }
        return tempObject;
    }

    private static String getObjStr(String realKey, Map<String, Object> fieldNameAndValue) {
        Object obj = getObj(realKey, fieldNameAndValue);
        return obj == null ? StringUtils.EMPTY : obj.toString();
    }

    /**
     * 通过对象的get方法取值
     */
    public static Object getValueFromGetObject(Object tempObject, String fieldName) {
        if (tempObject == null || StringUtils.isEmpty(fieldName)) {
            return null;
        }

        Method getMethod = getGetMethod(tempObject.getClass(), fieldName);
        if (getMethod == null) {
            return null;
        }

        try {
            return getMethod.invoke(tempObject);
        } catch (IllegalAccessException | InvocationTargetException e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }

    }

    /**
     * 获取对象的get方法
     *
     */
    public static Method getGetMethod(Class<?> clazz, String fieldName) {
        try {
            String methodName = "get" + fieldName.substring(0, 1).toUpperCase() + fieldName.substring(1);
            return clazz.getMethod(methodName);
        } catch (NoSuchMethodException e) {
            LOGGER.error(e.getMessage(), e);
            return null;
        }
    }

    /**
     * 获取参数Map集合
     *
     */
    public static Map<String, Object> getFieldNameAndValue(ProceedingJoinPoint joinPoint) {
        Map<String, Object> param = new HashMap<>();
        Object[] paramValues = joinPoint.getArgs();
        String[] paramNames = ((MethodSignature) joinPoint.getSignature()).getParameterNames();
        for (int i = 0; i < paramNames.length; i++) {
            param.put(paramNames[i], paramValues[i]);
        }
        return param;
    }
}
